package top.hcode.hoj.manager.coll;

import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.Cached;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
import org.nutz.lang.Times;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import top.hcode.hoj.dao.CollQuestionDao;
import top.hcode.hoj.dao.CollQuestionOptionDao;
import top.hcode.hoj.dao.CollUserAnswerDao;
import top.hcode.hoj.dao.CollUserStatDao;
import top.hcode.hoj.dao.collect.ItechCollDao;
import top.hcode.hoj.dao.org.OrgDao;
import top.hcode.hoj.dao.org.OrgUserDao;
import top.hcode.hoj.pojo.dto.coll.CollQuestionAnswerDTO;
import top.hcode.hoj.pojo.dto.coll.QueryCollQuestionDTO;
import top.hcode.hoj.pojo.entity.CollUserStat;
import top.hcode.hoj.pojo.entity.coll.CollQuestion;
import top.hcode.hoj.pojo.entity.coll.CollUserAnswer;
import top.hcode.hoj.pojo.entity.coll.ItechCollection;
import top.hcode.hoj.pojo.vo.coll.CollQuestionUserAnswerStatusVO;
import top.hcode.hoj.pojo.vo.coll.CollQuestionVO;
import top.hcode.hoj.utils.Constants;
import top.hcode.hoj.utils.ConvertUtil;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author orangej
 * @since 2025/2/9
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class ItechCollQuestionManager {
    private final StringRedisTemplate redisTemplate;

    private final OrgUserDao orgUserDao;
    private final OrgDao orgDao;

    private final ItechCollDao collDao;
    private final CollQuestionDao questionDao;
    private final CollQuestionOptionDao questionOptionDao;
    private final CollUserAnswerDao userAnswerDao;
    private final CollUserStatDao userStatDao;

    @Cached(cacheType = CacheType.BOTH, expire = 120)
    public List<CollQuestionVO> queryQuestionList(QueryCollQuestionDTO dto) {
        // 检查活动是否开始了
        if (!isCollExamStarted(dto.getCollId())) {
            return new ArrayList<>();
        }

        // 设置查询条件
        QueryWrapper<CollQuestion> query = new QueryWrapper<>();
        query.eq("coll_id", dto.getCollId()).orderByAsc("sort_order");

        // 查询表记录 CollQuestion
        List<CollQuestion> dboList = questionDao.list(query);

        // 转为 vo
        return dboList.stream().map(ConvertUtil::toQuestionVO).collect(Collectors.toList());
    }


    @Cached(cacheType = CacheType.BOTH, expire = 120)
    public CollQuestionVO getQuestionDetail(String questionId) {
        CollQuestion dbo = questionDao.getById(questionId);
        if (dbo == null) {
            return null;
        }

        // 检查活动是否开始了
        if (!isCollExamStarted(dbo.getCollId())) {
            throw new IllegalArgumentException("测评未开始");
        }

        CollQuestionVO vo = ConvertUtil.toQuestionVO(dbo);
        if (Constants.QuestionType.isChoice(dbo.getQuestionType())) {
            vo.setOptionList(questionOptionDao.queryOptionVO(questionId));
        }
        return vo;
    }

    /**
     * 查询个人答题情况
     */
    public CollQuestionUserAnswerStatusVO getUserAnswerStatus(String uid, String collId) {
        CollQuestionUserAnswerStatusVO vo = new CollQuestionUserAnswerStatusVO();

        // 查询已答题目id列表
        vo.setAnsweredQuestionIdList(userAnswerDao.queryAnsweredQuestionIds(uid, collId));

        // 查询开始和结束答题时间
        CollUserStat userStat = userStatDao.getByUserIdAndCollId(uid, collId);
        if (userStat != null) {
            vo.setStartAnswerAt(userStat.getStartAnswerAt());
            vo.setStopAnswerAt(userStat.getStopAnswerAt());
            if (userStat.getSubmitAt() != null) {
                vo.setSubmitAt(userStat.getSubmitAt());
            }
        }
        return vo;
    }

    /**
     * 提交答案（批量接口），一次提交所有题目的答案
     */
    @Transactional
    public void submitAnswer(String uid, String collId, List<CollQuestionAnswerDTO> submiteAnswerList) {
        CollUserStat userStat = userStatDao.getByUserIdAndCollId(uid, collId);
        if (userStat == null || userStat.getStartAnswerAt() == null) {
            throw new IllegalArgumentException("请先开始答题");
        }
        if (userStat.getSubmitAt() != null) {
            throw new IllegalArgumentException("您已提交答案，请勿重复提交");
        }

        String classId = orgUserDao.getStudentClassId(uid);

        // 答案可能为空
        if (submiteAnswerList == null) {
            submiteAnswerList = new ArrayList<>();
        }

        List<CollUserAnswer> userAnswerList = new ArrayList<>();
        for (CollQuestionAnswerDTO dto : submiteAnswerList) {
            String questionId = dto.getQuestionId();
            String answer = StrUtil.nullToDefault(dto.getAnswer(), "").trim();  // 填空题可能有空格

            boolean isMultiChoice = questionDao.isMultiChoice(questionId);
            if (isMultiChoice) {
                answer = sortIdInAnswer(answer);
            }

            CollUserAnswer dbo = new CollUserAnswer();
            dbo.setCollId(collId);
            dbo.setUserId(uid);
            dbo.setClassId(classId);
            dbo.setQuestionId(questionId);
            dbo.setScore(BigDecimal.ZERO);
            dbo.setJudgeState(0);
            dbo.setAnswer(answer);

            userAnswerList.add(dbo);
        }

        // 保存到 coll_user_answer 表
        if (Lang.isNotEmpty(userAnswerList)) {
            userAnswerDao.saveBatch(userAnswerList);
        }

        // 保存提交时间
        userStatDao.updateSubmitAtById(userStat.getId());
    }

    /**
     * 开始答题，更新 coll_user_stat.start_answer_at，设置开始答题时间和结束答题时间
     */
    public void startAnswer(String uid, String collId) {
        String key = "coll:start-answer:" + uid + ":" + collId;
        if (BooleanUtil.isTrue(redisTemplate.hasKey(key))) {
            throw new IllegalArgumentException("请勿重复提交");
        }

        try {
            redisTemplate.opsForValue().set(key, "1", 10, TimeUnit.SECONDS);

            ItechCollection coll = collDao.getByIdCache(collId);
            if (coll == null) {
                throw new IllegalArgumentException("活动不存在");
            }
            // 测评未开始
            if (coll.getExamStart() != null && coll.getExamStart().after(new Date())) {
                throw new IllegalArgumentException("测评未开始");
            }
            // 测评已结束
            if (coll.getExamEnd() != null && coll.getExamEnd().before(new Date())) {
                throw new IllegalArgumentException("测评已结束");
            }

            CollUserStat dbo = userStatDao.getByUserIdAndCollId(uid, collId);
            if (dbo != null && dbo.getSubmitAt() != null) {
                throw new IllegalArgumentException("您已提交答案，请勿重复操作！提交时间 " + Times.sDT(dbo.getSubmitAt()));
            }
            if (dbo != null && dbo.getStartAnswerAt() != null) {
                throw new IllegalArgumentException("您已开始答题，请勿重复操作！开始时间 " + Times.sDT(dbo.getStartAnswerAt()));
            }
            if (dbo == null) {
                dbo = new CollUserStat();
                dbo.setUserId(uid);
                dbo.setClassId(orgUserDao.getStudentClassId(uid));
                dbo.setClassName(orgDao.getOrgNameById(dbo.getClassId()));
                dbo.setUserName(orgUserDao.getRealNameByUid(uid));
                dbo.setCollId(collId);
            }

            dbo.setStartAnswerAt(new Date());

            int examMinutes = collDao.getExamMinutes(collId);   // 活动测试时长
            dbo.setStopAnswerAt(Times.nextMinute(dbo.getStartAnswerAt(), examMinutes));
            userStatDao.saveOrUpdate(dbo);
        } finally {
            redisTemplate.delete(key);
        }
    }

    // 对多选题的答案内容进行排序处理，方便判题
    String sortIdInAnswer(String answer) {
        if (Strings.isBlank(answer) || !answer.contains(",")) {
            return answer;
        }
        String[] arr = answer.split(",");
        Arrays.sort(arr);
        return String.join(",", arr);
    }

    /**
     * 检查活动测评是否开始了，没有活动或者活动未开始返回 false
     */
    private boolean isCollExamStarted(String collId) {
        ItechCollection coll = collDao.getByIdCache(collId);
        return coll != null && coll.getExamStart() != null && coll.getExamStart().before(new Date());
    }

}
